package com.diaohw.platform.framework.web.interceptor;

import cn.hutool.extra.spring.SpringUtil;
import com.diaohw.platform.common.LogProcessAPI;
import com.diaohw.platform.common.obj.log.RequestLog;
import com.diaohw.platform.common.obj.login.LoginUser;
import com.diaohw.platform.common.obj.thread.LoginThread;
import com.diaohw.platform.common.obj.thread.RequestThread;
import com.diaohw.platform.common.properties.LogProperties;
import com.diaohw.platform.framework.web.OperatorLog;
import com.diaohw.platform.framework.web.util.IpUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

import static com.diaohw.platform.common.constant.RequestConstant.*;
import static com.diaohw.platform.common.obj.thread.RequestThread.*;

/**
 * @author 刁海文
 * @Remark Request 链路拦截操作
 * @since 2022/8/8 9:28
 */
@Component
public class WebMvcInterceptor implements HandlerInterceptor {

    private final Logger logger = LoggerFactory.getLogger("Web Mvc Interceptor");

    @Resource
    private LogProperties logProperties;

    private Optional<LogProcessAPI> logProcessAPI;

    @PostConstruct
    public void init() {
        if (!logProperties.isHttp()) {
            logProcessAPI = Optional.empty();
        }
        try {
            LogProcessAPI bean = SpringUtil.getBean(LogProcessAPI.class);
            logProcessAPI = Optional.ofNullable(bean);
        } catch (Exception e) {
            logger.warn("LogProcessAPI is not Register...");
            logProcessAPI = Optional.empty();
        }
    }

    @Override
    public boolean preHandle(@Nullable HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Object handler) {
        assert request != null;

        String requestId = request.getHeader(REQUEST_ID);
        String linkId = request.getHeader(LINK_ID);
        String authorization = request.getHeader(AUTHORIZATION);
        RequestThread cache = initializationBase(requestId, linkId, authorization);

        if (initializationOperatorLog(cache, handler)) {
            return true;
        }
        if (isEnable()) {
            setRequestInfo(request);
        }

        return true;
    }

    @Override
    public void afterCompletion(@Nullable HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Object handler, Exception ex) {
        RequestLog log = RequestThread.getThreadLocal();

        if (isRequest()) {
            String str = log.getRequestData().length() > 100 ? log.getRequestData().substring(0, 97) + "..." : log.getRequestData();
            logger.info("""
                    
                    ==============================================================================
                    RequestId:[{}]
                    Method   :[{}_{}]
                    Parameter:[{}]
                    Duration :[{}] ms
                    ==============================================================================
                    """, log.getRequestId(), log.getMethod(), log.getUri(), str, log.getElapsed());
        }

        clear();
        LoginUser user = LoginThread.clear();
        if (!logProperties.http(log.getMethod())) {
            return;
        }
        log.setRequestIp(IpUtil.addr());

        if (user != null) {
            log.setLoginUsername(user.getUsername());
            log.setLoginNickname(user.getNickname());
            log.setLoginId(user.getLoginId());
        }

        // 发送消息
        logProcessAPI.ifPresent(api -> api.http(log));

    }


    // 链路日志初始化
    private boolean initializationOperatorLog(RequestThread cache, Object handler) {
        if (!(handler instanceof HandlerMethod method)) {
            return true;
        }
        Optional<Operation> operation = Optional.ofNullable(method.getMethodAnnotation(Operation.class));
        OperatorLog log = Optional.ofNullable(method.getMethodAnnotation(OperatorLog.class))
                .orElseGet(() -> method.getBeanType().getAnnotation(OperatorLog.class));

        Tag tag = method.getBeanType().getAnnotation(Tag.class);
        String str = "";
        if (tag != null) {
            str = tag.name();
        }
        cache.setEnable(Optional.ofNullable(log).map(OperatorLog::enable).orElse(Boolean.TRUE));

        cache.setRequest(Optional.ofNullable(log).map(OperatorLog::request).orElse(Boolean.TRUE));

        cache.setResponse(Optional.ofNullable(log).map(OperatorLog::response).orElse(Boolean.TRUE));
        String elseVal = log == null ? "" : log.describe();
        cache.getLog().setDescription(str + operation.map(Operation::summary).orElse(elseVal));
        return false;
    }


    private void setRequestInfo(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, Object> object = new HashMap<>();
        parameterMap.forEach((name, value) -> {
            if (value.length == 1) {
                object.put(name, value[0]);
            } else {
                object.put(name, value);
            }
        });

        putRequestRemote(request.getMethod(), request.getRequestURI(), object);

    }

}
